package com.creditrank.crcomponent;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

/*
 * The code for CreditRank Calculator module goes in here
 * 
 * @author Swarnim Kulkarni
 * */

public class CRCalculator {
	
	Map<String,String> graph;
	Map<String,String> invertedGraph;
	Map<String,Double> goodScores;
	Map<String,Double> badScores;
	Map<Integer,Double> creditScores;
	Map<String, Double> pageRankScores;
	List<String> spam;
	
	static final double DEFAULT_SCORE = 1.0;
	static final double ALPHA = 0.85;
	static final int ITERATIONS = 10;
	
	CRCalculator(){
		
		creditScores = new HashMap<Integer,Double>();
		pageRankScores = new HashMap<String,Double>();
	}
	
	List<String> initializeSpam(){
		
		System.out.println("Initializing spam");
		
		List<String> spamList = new ArrayList<String>();
		
		//Sample spam list
		String spamLinks = "0 5 7 8 12 20 25 29 36 45 47 52 56 59 77 79 80 81 90 99";
		
		StringTokenizer stk = new StringTokenizer(spamLinks);
		
		while(stk.hasMoreTokens())
			spamList.add(stk.nextToken().trim());
		
		return spamList;		
	}
	
	Map<String,String> readGraph(String fileName){		
		
		System.out.println("Now reading graph");
		
		Map<String,String> graph = new HashMap<String,String>();
		
		String line;
		
		String node;
		String links;
				
		try{
			FileReader fr = new FileReader(fileName);
			BufferedReader br = new BufferedReader(fr);
			
			line = br.readLine();
			
			while(line!=null){
				
				node = line.substring(0, line.indexOf(" "));				
				links = line.substring(line.indexOf(">"));							
				
				links = links.replaceAll(">", " ");				
				
				graph.put(node, links);
				
				line = br.readLine();
			}
		}catch(Exception e){e.printStackTrace();}
		
		return graph;		
	}
	
	Map<String,String> invertGraph(Map<String,String> graph){
		
		Map<String,String> invertedGraph = new HashMap<String,String>();
		
		String node="";
		String outlinks="";
		
		Set<String> tempKeySet;
		Iterator<String> tempNodeItr;
		String tempNode;
		String tempLinks;
		
		Set<String> keySet = graph.keySet();
		Iterator<String> nodeItr = keySet.iterator();
		
		while(nodeItr.hasNext()){
			
			outlinks="";
			node = nodeItr.next();
			
			tempKeySet = graph.keySet();
			tempNodeItr = keySet.iterator();
			
			while(tempNodeItr.hasNext()){
				
				tempNode = tempNodeItr.next();
				tempLinks = graph.get(tempNode);
				
				if(tempLinks.trim().contains(node.trim())){
					
					outlinks = outlinks+" "+tempNode;
				}
			}			
			outlinks = outlinks.trim();			
			invertedGraph.put(node, outlinks);			
		}		
				
		return invertedGraph;		
	}
	
	Map<String,Double> calculateGoodScores(Map<String,String> graph, Map<String,String> invertedGraph){
		
		Map<String,Double> tempMap = new HashMap<String,Double>();
		Map<String,Double> goodScores = new HashMap<String,Double>();
		
		String node;
		String linksList;
		String link;
		double fractionScore=0.0;
		double totalScore=0.0;
		
		double nonSpamRatio;
		int d;
		
		goodScores = initializeScores(goodScores,graph);
		
		for(int i=0;i<ITERATIONS;i++){
			
			Iterator<String> itr = graph.keySet().iterator();
			
			tempMap.clear();
			
			while(itr.hasNext()){
				
				node = itr.next();
				linksList = invertedGraph.get(node);
				
				if(spam.contains(node))
					d=0;					
				else					
					d=1;
									
				StringTokenizer stk = new StringTokenizer(linksList);
				fractionScore = 0.0;
				totalScore = 0.0;
				
				while(stk.hasMoreTokens()){
					
					link = stk.nextToken();
					fractionScore += goodScores.get(link)/noOfNodes(graph.get(link));										
				}
				nonSpamRatio = (double)(graph.size()-spam.size())/graph.size();
				totalScore = ALPHA*fractionScore*nonSpamRatio+((1-ALPHA)*d);
				tempMap.put(node, totalScore);
				 
			}
			goodScores.clear();
			goodScores.putAll(tempMap);		// update scores
		}	
				
		return goodScores;
		
	}
	
	Map<String,Double> calculateBadScores(Map<String,String> graph, Map<String,String> invertedGraph){
		
		Map<String,Double> tempMap = new HashMap<String,Double>();
		Map<String,Double> badScores = new HashMap<String,Double>();
		
		String node;
		String linksList;
		String link;
		double fractionScore=0.0;
		double totalScore=0.0;
		
		double totalSpamRatio;		// total spam / total links
		double localSpamRatio;		// links to spam from that node / total links from that node
		int d;
		
		badScores = initializeScores(badScores,graph);
		
		for(int i=0;i<ITERATIONS;i++){
			
			Iterator<String> itr = graph.keySet().iterator();
			
			tempMap.clear();
			
			while(itr.hasNext()){
				
				node = itr.next();
				linksList = graph.get(node);
				
				if(spam.contains(node))
					d=0;
				else
					d=1;
				
				StringTokenizer stk = new StringTokenizer(linksList);
				fractionScore = 0.0;
				totalScore = 0.0;
				
				while(stk.hasMoreTokens()){
					
					link = stk.nextToken();
					fractionScore += badScores.get(link)/noOfNodes(invertedGraph.get(link));										
				}
				totalSpamRatio = (double)(spam.size())/graph.size();
				localSpamRatio = (double)noOfSpamNodes(graph.get(node))/noOfNodes(graph.get(node));
				totalScore = ALPHA*fractionScore*totalSpamRatio+((1-ALPHA)*(1-d)*localSpamRatio);
				tempMap.put(node, totalScore);
				 
			}
			badScores.clear();
			badScores.putAll(tempMap);		// update scores
		}	
				
		return badScores;
		
	}
	
	Map<String,Double> calculatePageScores(Map<String,String> graph, Map<String,String> invertedGraph){
		
		Map<String,Double> tempMap = new HashMap<String,Double>();
		Map<String,Double> pageScores = new HashMap<String,Double>();
		
		String node;
		String linksList;
		String link;
		double fractionScore=0.0;
		double totalScore=0.0;		
		
		pageScores = initializeScores(pageScores,graph);
		
		for(int i=0;i<ITERATIONS;i++){
			
			Iterator<String> itr = graph.keySet().iterator();
			
			tempMap.clear();
			
			while(itr.hasNext()){
				
				node = itr.next();
				linksList = invertedGraph.get(node);
																	
				StringTokenizer stk = new StringTokenizer(linksList);
				fractionScore = 0.0;
				totalScore = 0.0;
				
				while(stk.hasMoreTokens()){
					
					link = stk.nextToken();
					fractionScore += pageScores.get(link)/noOfNodes(graph.get(link));										
				}
				totalScore = ALPHA*fractionScore+((1-ALPHA)/graph.size());
				tempMap.put(node, totalScore);
				 
			}
			pageScores.clear();
			pageScores.putAll(tempMap);		// update scores
		}	
				
		return pageScores;
		
	}
	
	int noOfNodes(String list){
				
		StringTokenizer stk = new StringTokenizer(list);
		return stk.countTokens();
	}
	
	int noOfSpamNodes(String list){
		
		int count=0;
		
		StringTokenizer stk = new StringTokenizer(list);
		
		while(stk.hasMoreTokens()){
			if(spam.contains(stk.nextToken()))
				count++;
		}
		return count;
	}
	
	private Map<String,Double> initializeScores(Map<String,Double> scores, Map<String,String> graph){
		
		Set<String> keySet = graph.keySet();
		Iterator<String> nodeItr = keySet.iterator();
		
		scores.clear();
		while(nodeItr.hasNext()){
			
			scores.put(nodeItr.next().trim(), DEFAULT_SCORE);
		}
		
		return scores;
		
	}
	
//  TODO: insert function to sort hashtable automatically by value
	
	public static void main(String args[]){
		
		CRCalculator cr1 = new CRCalculator();
		
		cr1.spam = cr1.initializeSpam();
		cr1.graph = cr1.readGraph("data/Graph.txt");
		cr1.invertedGraph = cr1.invertGraph(cr1.graph);	
		
		cr1.goodScores = cr1.calculateGoodScores(cr1.graph, cr1.invertedGraph);
		cr1.badScores = cr1.calculateBadScores(cr1.graph, cr1.invertedGraph);
		cr1.pageRankScores = cr1.calculatePageScores(cr1.graph, cr1.invertedGraph);
				
		for(int i=0;i<cr1.graph.size();i++){
						
			cr1.creditScores.put(i, cr1.goodScores.get(Integer.toString(i))-cr1.badScores.get(Integer.toString(i)));
		}
		
		Iterator<Integer> itr = cr1.creditScores.keySet().iterator();
		int node;
		
		while(itr.hasNext()){
			
			node = itr.next();			
						
			if(cr1.spam.contains(((Integer)node).toString()))
				System.out.println(node+","+cr1.creditScores.get(node)+"  <---");
			else
				System.out.println(node+","+cr1.creditScores.get(node));
			
		}
		
		System.out.println();
		System.out.println("*****************************");
		System.out.println("PageRank Scores :");
		System.out.println("*****************************");
		Iterator<String> itr1 = cr1.pageRankScores.keySet().iterator();
		String node1;
		
		while(itr1.hasNext()){
			
			node1 = itr1.next();			
									
			if(cr1.spam.contains(node1.trim()))
				System.out.println(node1+","+cr1.pageRankScores.get(node1)+"  <---");
			else
				System.out.println(node1+","+cr1.pageRankScores.get(node1));
			
		}
		
	}	
}
